package com.globefrog.framework.web.wrapper;

import com.globefrog.framework.log.util.IOUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Enumeration;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.springframework.util.Assert;


/**
 * http请求包装类.
 *
 * @author : caijinbang
 * @date : Create in 2021-01-14
 */
public class HttpRequestWrapper extends HttpServletRequestWrapper {

  /**
   * http请求方法.
   */
  public static final String METHOD_GET = "GET";
  public static final String METHOD_POST = "POST";
  public static final String METHOD_HEAD = "HEAD";
  public static final String METHOD_PUT = "PUT";
  public static final String METHOD_DELETE = "DELETE";

  protected BufferedReader reader;
  protected ServletInputStream inputStream;
  protected String originalBody;
  protected String body;

  public HttpRequestWrapper(HttpServletRequest request) throws IOException {
    super(request);
    // 预加载请求体
    preLoadBody(request);
  }

  /**
   * 预加载请求体.
   *
   * @param request 请求
   * @throws IOException IO异常
   */
  private void preLoadBody(HttpServletRequest request) throws IOException {
    if (isPreLoadBody()) {
      Charset charset = Charset.forName(this.getCharacterEncoding());
      ServletInputStream originalInputStream = request.getInputStream();
      Assert.notNull(originalInputStream, "POST or PUT should have input steam as body");
      byte[] bodyBytes = IOUtils.bytes(originalInputStream);
      this.originalBody = new String(bodyBytes, charset);
      this.body = this.getParameter("_body");
      if (this.body == null) {
        this.body = this.originalBody;
      }
      this.inputStream = new RequestInputStream(this.body.getBytes(charset));
    }
  }

  public boolean isPreLoadBody() {
    return containsBody() && !containMultipart();
  }

  /**
   * 是否包含请求体,只有post和get请求才有请求体.
   *
   * @return 是否包含请求体
   */
  private boolean containsBody() {
    String originalMethod = super.getMethod().toUpperCase();
    return METHOD_POST.equals(originalMethod) || METHOD_PUT.equals(originalMethod);
  }

  /**
   * contentType是否包含multipart.
   *
   * @return contentType是否包含multipart
   */
  private boolean containMultipart() {
    String contentType = getContentType();
    return contentType != null && contentType.toLowerCase().startsWith("multipart/");
  }


  @Override
  public final String getContentType() {
    String overrideContentType = this.getParameter("_contentType");
    return overrideContentType != null ? overrideContentType : super.getContentType();
  }

  @Override
  public String getHeader(String name) {
    return super.getHeader(name);
  }

  @Override
  public Enumeration<String> getHeaders(String name) {
    return super.getHeaders(name);
  }

  @Override
  public String getMethod() {
    String overrideMethod = this.getParameter("_method");
    return overrideMethod != null ? overrideMethod : super.getMethod();
  }

  @Override
  public ServletInputStream getInputStream() throws IOException {
    return this.inputStream != null ? this.inputStream : super.getInputStream();
  }

  @Override
  public BufferedReader getReader() throws IOException {
    if (this.reader == null) {
      this.reader = new BufferedReader(
          new InputStreamReader(this.inputStream, this.getCharacterEncoding()));
    }
    return this.reader;
  }

  @Override
  public boolean isSecure() {
    return "https".equals(this.getScheme());
  }

  @Override
  public int getServerPort() {
    return super.getServerPort();
  }

  public String getBody() {
    Assert.isTrue(!this.containMultipart(), "not support get body for multipart request");
    return this.body;
  }

  public String getOriginalBody() {
    return originalBody;
  }
}
